#include "StdAfx.h"
#include "Red.h"

Red::Red(void)
{
	costoSol = cantNodos = cantNodosSteiner = cantNodosTerminales = cantAristas = 0;
}

Red::Red(const Red *r)
{
	this->cantAristas = r->cantAristas;
	this->cantNodos = r->cantNodos;
	this->cantNodosSteiner = r->cantNodosSteiner;
	this->cantNodosTerminales = r->cantNodosTerminales;
	this->costoSol = r->costoSol;
	
	//copia limpia
	this->nodos = r->nodos;
	this->nodosTerminales = r->nodosTerminales;
	this->matrizAdyacencias = r->matrizAdyacencias;
	this->listaAdyacencias = r->listaAdyacencias;
}

int Red::getCantidadNodos() 
{
	return cantNodos;
}

int Red::getCantidadNodosTerminales() 
{
	return cantNodosTerminales;
}

int Red::getCantidadNodosSteiner() 
{
	return cantNodosSteiner;
}

InfoVerticeSet& Red::getNodos()
{
	return nodos;
}

InfoVertice Red::getInfoVertice(int idNodo)
{
	return *(nodos.find(InfoVertice(idNodo, false))); //(infoVert solo para buscar)
}

int Red::getCantidadAristas() 
{
	return cantAristas;
}

InfoArista Red::getInfoArista(int i, int j)
{
	return matrizAdyacencias[i][j];
}

list<InfoArista>& Red::getAdyacentesNodo(int idNodo)
{
	return listaAdyacencias[idNodo];
}

InfoVerticeSet& Red::getNodosTerminales()
{
	return nodosTerminales;
}

void Red::leer(string & archivo) 
{
	FILE *f = fopen(archivo.c_str(), "r");

	if (f == NULL)
	{
		printf("No se encontro el archivo de red.\n");
		exit(1);
	}

	fscanf(f,"%d", &cantNodosTerminales);
	fscanf(f,"%d", &cantNodosSteiner);
	fscanf(f,"%d", &cantAristas);

	cantNodos = cantNodosTerminales + cantNodosSteiner;
	
	listaAdyacencias.resize(cantNodos);
	matrizAdyacencias.resize(cantNodos);

	for (vector<vector<InfoArista>>::size_type i = 0; i < matrizAdyacencias.size(); i++) {
		matrizAdyacencias[i].resize(cantNodos); //init infoAristas con pair.first = -1 (no existentes)
	}
	
	int id, esTerminal;
	for (int i=0; i<cantNodos; i++)
	{
		fscanf(f, "%d", &id);
		fscanf(f, "%d", &esTerminal);

		InfoVertice iv(id-1, esTerminal);
		nodos.insert(iv);
		if (esTerminal) {
			nodosTerminales.insert(iv);
		}
	}

	InfoArista ia;
	int id1, id2;
	double costo;
	for (int i=0; i<cantAristas; i++)
	{
		fscanf(f, "%d %d %Lf", &id1, &id2, &costo);

		ia.vertices.first = id1-1;
		ia.vertices.second = id2-1;
		ia.costo = costo;

		matrizAdyacencias[ia.vertices.first][ia.vertices.second] = ia;
		listaAdyacencias[ia.vertices.first].push_back(ia);

		//bidir:
		ia.vertices.first = id2-1;
		ia.vertices.second = id1-1;

		matrizAdyacencias[ia.vertices.first][ia.vertices.second] = ia;
		listaAdyacencias[ia.vertices.first].push_back(ia);
	}

	fclose(f);

	cantAristas *= 2; //bidir
}

//de construccion
Red::Red(int cantTotalNodos) 
{
	costoSol = cantNodos = cantNodosSteiner = cantNodosTerminales = cantAristas = 0;
	costoSol = 0;
	listaAdyacencias.resize(cantTotalNodos);
	matrizAdyacencias.resize(cantTotalNodos);

	for (vector<vector<InfoArista>>::size_type i = 0; i < matrizAdyacencias.size(); i++) {
		matrizAdyacencias[i].resize(cantTotalNodos); //init infoAristas con pair.first = -1 (no existentes)
	}
}

double Red::getCosto()
{
	return costoSol;
}

bool Red::existeNodo(InfoVertice & iv)
{
	return (nodos.count(iv) == 1);
}

void Red::removerNodos(InfoVerticeSet &nodosSet)
{
	for (InfoVerticeSet::iterator it = nodosSet.begin(); it != nodosSet.end(); it++) {
		eliminarNodo(*it);
	}
}

//por ahora solo se usa ante caso especial en construccion de ciclo inicial
void Red::eliminarArista(InfoArista & ia)
{
	matrizAdyacencias[ia.vertices.first][ia.vertices.second].vertices.first = -1;
	matrizAdyacencias[ia.vertices.second][ia.vertices.first].vertices.first = -1;
	listaAdyacencias[ia.vertices.first].remove(ia);
	listaAdyacencias[ia.vertices.second].remove(InfoArista(ia.vertices.second, ia.vertices.first, ia.costo));
	cantAristas -= 2;
	costoSol -= 2*ia.costo;
}

void Red::eliminarNodo(InfoVertice iv)
{
	nodos.erase(iv);
	if (iv.esTerminal) {
		nodosTerminales.erase(iv);
		cantNodosTerminales--;
	}
	else {
		cantNodosSteiner--;
	}

	double sustraerCosto = 0;
	//eliminacion de aristas incidentes al nodo
	for (list<InfoArista>::iterator it = listaAdyacencias[iv.idVertice].begin();
		it != listaAdyacencias[iv.idVertice].end(); it++) {
		
			sustraerCosto += matrizAdyacencias[iv.idVertice][it->vertices.second].costo;
			matrizAdyacencias[iv.idVertice][it->vertices.second].vertices.first = -1;
			matrizAdyacencias[it->vertices.second][iv.idVertice].vertices.first = -1;
			eliminarAristaDeLista(listaAdyacencias[it->vertices.second], iv.idVertice);
	}

	cantAristas -= listaAdyacencias[iv.idVertice].size()*2; //no dirigido, bidir
	costoSol -= sustraerCosto*2;
	listaAdyacencias[iv.idVertice].clear();

	cantNodos--;
}

void Red::eliminarAristaDeLista(list<InfoArista> &listaAdy, int idNodo)
{
	for (list<InfoArista>::iterator it = listaAdy.begin(); it != listaAdy.end(); it++) {
		if (it->vertices.second == idNodo) {
			listaAdy.erase(it);
			break;
		}
	}
}

void Red::agregarKeyPath(Path &p, Red *grafoBase)
{
	vector<InfoVertice> &camino = p.getPath();

	agregarNodo(camino[0]);
	for (int i=1; i<camino.size(); i++) {
		agregarNodo(camino[i]);
		agregarArista(camino[i-1], camino[i], 
			grafoBase->getInfoArista(camino[i-1].idVertice, camino[i].idVertice).costo);
	}
	
	costoSol += p.getCosto();
}

void Red::agregarNodo(InfoVertice &iv)
{
	if (nodos.find(iv) == nodos.end()) {
		nodos.insert(iv);
		cantNodos++;
		if (iv.esTerminal) {
			nodosTerminales.insert(iv);
			cantNodosTerminales++;
		}
		else {
			cantNodosSteiner++;
		}
	}
}

void Red::agregarArista(InfoVertice &iv1, InfoVertice &iv2, double costo)
{
	//no se controla aristas repetidas, no deberia pasar (controlar por afuera si fuera posible)
	matrizAdyacencias[iv1.idVertice][iv2.idVertice].vertices.first = iv1.idVertice;
	matrizAdyacencias[iv1.idVertice][iv2.idVertice].vertices.second = iv2.idVertice;
	matrizAdyacencias[iv1.idVertice][iv2.idVertice].costo = costo;

	listaAdyacencias[iv1.idVertice].push_back(matrizAdyacencias[iv1.idVertice][iv2.idVertice]);

	matrizAdyacencias[iv2.idVertice][iv1.idVertice].vertices.first = iv2.idVertice;
	matrizAdyacencias[iv2.idVertice][iv1.idVertice].vertices.second = iv1.idVertice;
	matrizAdyacencias[iv2.idVertice][iv1.idVertice].costo = costo;

	listaAdyacencias[iv2.idVertice].push_back(matrizAdyacencias[iv2.idVertice][iv1.idVertice]);

	cantAristas += 2; //bidir
}


Red::~Red(void)
{
	matrizAdyacencias.clear();
	listaAdyacencias.clear();
}
